﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OracleClient;
using System.Data;
using System.Data.Common;

namespace DbSharp.Framework.Driver.FieldPersistence
{
    //http://www.cnblogs.com/qmxle/archive/2009/06/06/oracle_datatype_dotnet.html
    class OracleFieldPersistence : IFieldPersistence
    {
        #region IFieldPersistence<OracleType> members

        public List<int> ListSupportedScalarDataType
        {
            get
            {
                var list = new List<int>();
                list.Add((int)OracleType.Char);
                list.Add((int)OracleType.VarChar);
                list.Add((int)OracleType.NChar);
                list.Add((int)OracleType.NVarChar);
                list.Add((int)OracleType.Number);
                list.Add((int)OracleType.DateTime);
                list.Add((int)OracleType.Timestamp);
                list.Add((int)OracleType.TimestampLocal);
                list.Add((int)OracleType.TimestampWithTZ);
                list.Add((int)OracleType.IntervalDayToSecond);
                list.Add((int)OracleType.IntervalYearToMonth);
                list.Add((int)OracleType.Int32);
                list.Add((int)OracleType.Float);
                list.Add((int)OracleType.Double);
                list.Add((int)OracleType.RowId);

                return list;
            }
        }

        public List<int> ListSupportedRecordsetDataType
        {
            get
            {
                var list = new List<int>();
                list.Add((int)OracleType.Cursor);
                return list;
            }
        }

        public string ConvertToString(object value, int valueType)
        {
            var valueType1 = (OracleType)valueType;
            var exceptionFmt = "{0} type is not supported.";
            var exceptionMsg = string.Format(exceptionFmt, valueType1.ToString());
            if (!ListSupportedScalarDataType.Contains(valueType))
            {
                throw new NotSupportedException(exceptionMsg);
            }

            string result;
            switch (valueType1)
            {
                case OracleType.Char:
                case OracleType.VarChar:
                case OracleType.NChar:
                case OracleType.NVarChar:
                case OracleType.Number:
                case OracleType.DateTime:
                case OracleType.Timestamp:
                case OracleType.TimestampLocal:
                case OracleType.TimestampWithTZ:
                case OracleType.IntervalDayToSecond:
                case OracleType.IntervalYearToMonth:
                case OracleType.Int32:
                case OracleType.Float:
                case OracleType.Double:
                case OracleType.RowId:
                    result = FieldPersistenceHelper.SafeConvertToString(value);
                    break;
                default:
                    throw new NotSupportedException(exceptionMsg);
            }
            return result;
        }

        public decimal RestoreStringToDecimal(string valueStr, int valueType)
        {
            var valueType1 = (OracleType)valueType;
            decimal result;
            switch (valueType1)
            {
                case OracleType.Number:
                    var number = OracleNumber.Parse(valueStr);
                    result = number.Value;
                    break;
                case OracleType.IntervalYearToMonth:
                case OracleType.Int32:
                case OracleType.Float:
                case OracleType.Double:
                    result = Convert.ToDecimal(valueStr);
                    break;
                default:
                    throw new NotSupportedException(string.Format("{0} type is not supported.", valueType1.ToString()));
            }
            return result;
        }

        public Devart.Data.Oracle.OracleNumber RestoreStringToLargeDecimal(string valueStr, int valueType)
        {
            OracleType valueType1 = (OracleType)valueType;
            Devart.Data.Oracle.OracleNumber result;
            switch (valueType1)
            {
                case OracleType.Number:
                case OracleType.IntervalYearToMonth:
                case OracleType.Int32:
                case OracleType.Float:
                case OracleType.Double:
                    result = Devart.Data.Oracle.OracleNumber.Parse(valueStr);
                    break;
                default:
                    throw new NotSupportedException(string.Format("{0} type is not supported.", valueType1.ToString()));
            }
            return result;
        }


        public int GetSpecificDbType(DbDataReader dataReader, int columnIndex)
        {
            //class: System.Data.OracleClient.OracleDataReader
            //method: public override string GetDataTypeName(int i)

            //var method = dataReader.GetType().GetMethod("GetDataTypeName");
            //var parameters = new object[] { columnIndex };
            //var typeName = (string)method.Invoke(dataReader, parameters);

            var reader = (System.Data.OracleClient.OracleDataReader)dataReader;
            var typeName = reader.GetDataTypeName(columnIndex);

            //The following constant values is taken in class of System.Data.OracleClient.MetaType
            const string N_BFILE = "BFILE";
            const string N_BLOB = "BLOB";
            const string N_CHAR = "CHAR";
            const string N_CLOB = "CLOB";
            const string N_DATE = "DATE";
            const string N_FLOAT = "FLOAT";
            const string N_INTEGER = "INTEGER";
            const string N_INTERVALDS = "INTERVAL DAY TO SECOND";
            const string N_INTERVALYM = "INTERVAL YEAR TO MONTH";
            const string N_LONG = "LONG";
            const string N_LONGRAW = "LONG RAW";
            const string N_NCHAR = "NCHAR";
            const string N_NCLOB = "NCLOB";
            const string N_NUMBER = "NUMBER";
            const string N_NVARCHAR2 = "NVARCHAR2";
            const string N_RAW = "RAW";
            const string N_REFCURSOR = "REF CURSOR";
            const string N_ROWID = "ROWID";
            const string N_TIMESTAMP = "TIMESTAMP";
            const string N_TIMESTAMPLTZ = "TIMESTAMP WITH LOCAL TIME ZONE";
            const string N_TIMESTAMPTZ = "TIMESTAMP WITH TIME ZONE";
            const string N_UNSIGNEDINT = "UNSIGNED INTEGER";
            const string N_VARCHAR2 = "VARCHAR2";

            var metaTypeMapping = new Dictionary<string, System.Data.OracleClient.OracleType>();
            metaTypeMapping.Add(N_BFILE, System.Data.OracleClient.OracleType.BFile);
            metaTypeMapping.Add(N_BLOB, System.Data.OracleClient.OracleType.Blob);
            metaTypeMapping.Add(N_CHAR, System.Data.OracleClient.OracleType.Char);
            metaTypeMapping.Add(N_CLOB, System.Data.OracleClient.OracleType.Clob);
            metaTypeMapping.Add(N_DATE, System.Data.OracleClient.OracleType.DateTime);
            metaTypeMapping.Add(N_FLOAT, System.Data.OracleClient.OracleType.Float);
            metaTypeMapping.Add(N_INTEGER, System.Data.OracleClient.OracleType.Int32);
            metaTypeMapping.Add(N_INTERVALDS, System.Data.OracleClient.OracleType.IntervalDayToSecond);
            metaTypeMapping.Add(N_INTERVALYM, System.Data.OracleClient.OracleType.IntervalYearToMonth);
            metaTypeMapping.Add(N_LONG, System.Data.OracleClient.OracleType.LongVarChar);
            metaTypeMapping.Add(N_LONGRAW, System.Data.OracleClient.OracleType.LongRaw);
            metaTypeMapping.Add(N_NCHAR, System.Data.OracleClient.OracleType.NChar);
            metaTypeMapping.Add(N_NCLOB, System.Data.OracleClient.OracleType.NClob);
            metaTypeMapping.Add(N_NUMBER, System.Data.OracleClient.OracleType.Number);
            metaTypeMapping.Add(N_NVARCHAR2, System.Data.OracleClient.OracleType.NVarChar);
            metaTypeMapping.Add(N_RAW, System.Data.OracleClient.OracleType.Raw);
            metaTypeMapping.Add(N_REFCURSOR, System.Data.OracleClient.OracleType.Cursor);
            metaTypeMapping.Add(N_ROWID, System.Data.OracleClient.OracleType.RowId);
            metaTypeMapping.Add(N_TIMESTAMP, System.Data.OracleClient.OracleType.Timestamp);
            metaTypeMapping.Add(N_TIMESTAMPLTZ, System.Data.OracleClient.OracleType.TimestampLocal);
            metaTypeMapping.Add(N_TIMESTAMPTZ, System.Data.OracleClient.OracleType.TimestampWithTZ);
            metaTypeMapping.Add(N_UNSIGNEDINT, System.Data.OracleClient.OracleType.UInt32);
            metaTypeMapping.Add(N_VARCHAR2, System.Data.OracleClient.OracleType.VarChar);

            System.Data.OracleClient.OracleType result;
            var found = metaTypeMapping.TryGetValue(typeName, out result);
            if (found)
                return (int)result;
            else
                return -1;

        }



        public int GetSpecificDbType(DbParameter dbParam)
        {
            var typ = ((System.Data.OracleClient.OracleParameter)dbParam).OracleType;
            return (int)typ;
        }


        public int NumberTypeWhenParseLiteral
        {
            get { return (int)(OracleType.Number); }
        }

        public bool IsStringType(int valueType)
        {
            bool result = false;
            var valueType1 = (OracleType)valueType;
            switch (valueType1)
            {
                case OracleType.Char:
                case OracleType.VarChar:
                case OracleType.NChar:
                case OracleType.NVarChar: 
                    result = true;
                    break;
                default:
                    result = false;
                    break;
            }

            return result;
        }


        #endregion
    }
}